﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Security.Cryptography;

namespace QuickDev.SSO
{
    /// <summary>
    /// SsoCipher
    /// </summary>
    public class SsoCipher
    {
        #region 3DES encryt/decypt methods

        /// <summary>
        /// EncryptCore
        /// </summary>
        /// <param name="bytes">bytes</param>
        /// <returns>des3.CreateEncryptor</returns>
        public static byte[] EncryptCore( byte[] bytes )
        {
            TripleDESCryptoServiceProvider des3 = new TripleDESCryptoServiceProvider();
            byte[] rgbKey = HexToBytes( ConfigurationManager.AppSettings[Constants.Des3KeyName] );
            return des3.CreateEncryptor( rgbKey , HexToBytes( Constants.Des3IV ) ).TransformFinalBlock( bytes , 0 , bytes.Length );
        }

        /// <summary>
        /// DecryptCore
        /// </summary>
        /// <param name="bytes">bytes</param>
        /// <returns>des3.CreateDecryptor</returns>
        public static byte[] DecryptCore( byte[] bytes )
        {
            TripleDESCryptoServiceProvider des3 = new TripleDESCryptoServiceProvider();
            byte[] rgbKey = HexToBytes( ConfigurationManager.AppSettings[Constants.Des3KeyName] );
            return des3.CreateDecryptor( rgbKey , HexToBytes( Constants.Des3IV ) ).TransformFinalBlock( bytes , 0 , bytes.Length );
        }


        /// <summary>
        /// Encrypt
        /// </summary>
        /// <param name="textToEncrypt">textToEncrypt</param>
        /// <returns>BytesToHex</returns>
        public static string Encrypt( string textToEncrypt )
        {
            return BytesToHex( EncryptCore( Encoding.Default.GetBytes( textToEncrypt ) ) );
        }


        /// <summary>
        /// Decrypt
        /// </summary>
        /// <param name="textToDecrypt">textToDecrypt</param>
        /// <returns>Encoding.Default.GetString</returns>
        public static string Decrypt( string textToDecrypt )
        {
            return Encoding.Default.GetString( DecryptCore( HexToBytes( textToDecrypt ) ) );
        }

        /// <summary>
        /// TryDecrypt
        /// </summary>
        /// <param name="textToDecrypt">textToDecrypt</param>
        /// <param name="textResult">textResult</param>
        /// <returns>bool successful</returns>
        public static bool TryDecrypt( string textToDecrypt , out string textResult )
        {
            textResult = null;
            byte[] bytes = null;
            if ( !TryParseHex( textToDecrypt , out bytes ) )
                return false;
            textResult = Encoding.Default.GetString( DecryptCore( bytes ) );
            return true;
        }

        #endregion

        #region SHA-1 hash methods

        /// <summary>
        /// SHA-1
        /// </summary>
        /// <param name="bytes">bytes</param>
        /// <returns>sha1.ComputeHash( bytes )</returns>
        public static byte[] HashCore( byte[] bytes )
        {
            //MD5CryptoServiceProvider MD5 = new MD5CryptoServiceProvider();
            //return MD5.ComputeHash(bytes);
            SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
            return sha1.ComputeHash( bytes );
        }

        /// <summary>
        /// Hash
        /// </summary>
        /// <param name="textToHash">textToHash</param>
        /// <returns>BytesToHex</returns>
        public static string Hash( string textToHash )
        {
            return BytesToHex( HashCore( Encoding.Default.GetBytes( textToHash ) ) );
        }

        #endregion

        #region Vector encrypt

        /// <summary>
        /// EncryptVector
        /// </summary>
        /// <param name="vector">vector</param>
        /// <returns>encrypt string</returns>
        public static string EncryptVector( params string[] vector )
        {
            if ( vector == null || vector.Length == 0 )
                return null;
            StringBuilder sb = new StringBuilder();
            for ( int i = 0 ; i < vector.Length ; i++ )
            {
                if ( !string.IsNullOrEmpty( vector[i] ) )
                {
                    sb.Append( Convert.ToBase64String( Encoding.Default.GetBytes( vector[i] ) ) );
                }
                if ( i != vector.Length - 1 )
                    sb.Append( Constants.Delimiter );
            }
            string digest = Hash( sb.ToString() );
            sb.Append( Constants.Delimiter );
            sb.Append( digest );
            return Encrypt( sb.ToString() );
        }

        /// <summary>
        /// TryParseVector
        /// </summary>
        /// <param name="encodedText">encodedText</param>
        /// <param name="vector">vector</param>
        /// <returns>bool successful</returns>
        public static bool TryParseVector( string encodedText , out string[] vector )
        {
            vector = null;
            if ( string.IsNullOrEmpty( encodedText ) || encodedText.Equals( Constants.InValidParameter ) )
                return false;
            string textDecoded = Decrypt( encodedText );
            string[] tempVector = textDecoded.Split( Constants.Delimiter );
            if ( tempVector.Length <= 1 )
                return false;
            string digest = tempVector[tempVector.Length - 1];
            /*byte[] digestBytes = null;
            if (!TryParseHex(digest, out digestBytes))
                return false;*/
            StringBuilder sb = new StringBuilder();
            for ( int i = 0 ; i < tempVector.Length - 1 ; i++ )
            {
                if ( !string.IsNullOrEmpty( tempVector[i] ) )
                {
                    sb.Append( tempVector[i] );
                }
                if ( i != tempVector.Length - 2 )
                    sb.Append( Constants.Delimiter );
            }
            if ( string.Compare( Hash( sb.ToString() ) , digest ) != 0 )
                return false;
            vector = new string[tempVector.Length - 1];
            for ( int i = 0 ; i < vector.Length ; i++ )
                vector[i] = string.IsNullOrEmpty( tempVector[i] ) ? null
                    : Encoding.Default.GetString( Convert.FromBase64String( tempVector[i] ) );
            return true;
        }

        #endregion

        #region Helper methods

        /// <summary>
        /// HexToBytes
        /// </summary>
        /// <param name="hexText">hexText</param>
        /// <returns>byte[] bytes</returns>
        public static byte[] HexToBytes( string hexText )
        {
            int size = ( int )hexText.Length / 2;
            byte[] bytes = new byte[size];
            for ( int i = 0 ; i < size ; i++ )
            {
                bytes[i] = byte.Parse( hexText.Substring( i * 2 , 2 ) , System.Globalization.NumberStyles.HexNumber );
            }
            return bytes;
        }

        /// <summary>
        /// TryParseHex
        /// </summary>
        /// <param name="hexText">hexText</param>
        /// <param name="bytes">bytes</param>
        /// <returns>bool successful</returns>
        public static bool TryParseHex( string hexText , out byte[] bytes )
        {
            bytes = null;
            if ( string.IsNullOrEmpty( hexText ) )
                return false;
            if ( hexText.Length % 2 != 0 )
                return false;
            int size = ( int )hexText.Length / 2;
            byte[] tmpBytes = new byte[size];
            for ( int i = 0 ; i < size ; i++ )
            {
                if ( !byte.TryParse( hexText.Substring( i * 2 , 2 ) , System.Globalization.NumberStyles.HexNumber ,
                    System.Globalization.NumberFormatInfo.InvariantInfo , out tmpBytes[i] ) )
                    return false;
            }
            bytes = tmpBytes;
            return true;
        }

        /// <summary>
        /// BytesToHex
        /// </summary>
        /// <param name="bytes">bytes</param>
        /// <returns>string</returns>
        public static string BytesToHex( byte[] bytes )
        {
            StringBuilder sb = new StringBuilder( bytes.Length * 2 );
            foreach ( byte b in bytes )
            {
                sb.AppendFormat( b.ToString( "x2" ) );
            }
            return sb.ToString();
        }

        #endregion
    }
}
